package com.dyh.quickdemo.demos.threBank;

import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/*
@Author=Joey Wheeler
@Date=2020/10/30 10:36
*/
@Slf4j
public class ThreBank {

    /*hystrix线程池是可以reset的*/

    Map<String,CustomerLoanMsg> applyloanMsgMap = new ConcurrentHashMap<>();//申请的贷款
    Map<String,Integer> realLoanMap = new ConcurrentHashMap<>();//贷款中
    Map<String,Semaphore> inLoanMap = new ConcurrentHashMap<>();

  //  static  int core = Runtime.getRuntime().availableProcessors();
    static  int core = 2;
    static ExecutorService cpuPool = Executors.newFixedThreadPool(core);
    static ExecutorService ioPool = Executors.newFixedThreadPool(core*2);


//    Map<String,Integer> inloanMap = new ConcurrentHashMap<>();//贷款中

    /*业务 申请贷款*/
    public void applyLoan(String customer,LoanType loanType,int count){
        CustomerLoanMsg customerLoanMsg = new CustomerLoanMsg();
        customerLoanMsg.setCount(count);
        customerLoanMsg.setLoanType(loanType);
        customerLoanMsg.setCustomer(customer);
        applyLoan(customerLoanMsg);
    }

    public ExecutorService findPool(LoanType loanType){
        if (loanType.equals(LoanType.IO))
            return ioPool;
        if (loanType.equals(LoanType.CPU))
            return cpuPool;
        return null;
    }

    public void submit(String customer,List<Runnable> tasks) throws InterruptedException, ExecutionException {
        CustomerLoanMsg customerLoanMsg = applyloanMsgMap.get(customer);
        ExecutorService pool = findPool(customerLoanMsg.getLoanType());
        int realLoan = realLoanMap.get(customer);
        Semaphore semaphore = inLoanMap.get(customer);
        int loanReturn = 0;
        if (semaphore==null){
            semaphore = new Semaphore(realLoan);
            inLoanMap.put(customer,semaphore);
        }
        for (Runnable task : tasks) {                //贷款全还完了 todo 改成Hystix的可以重置的信号量
            if (loanReturn==realLoan){
                semaphore.release(realLoan);//还清
                //semaphore = new Semaphore(realLoan);
                inLoanMap.put(customer,semaphore);
                loanReturn = 0;
            }
            log.info("before acquire"+semaphore.toString()+semaphore.availablePermits());
            semaphore.acquire();
            pool.submit(task);//.get();
            log.info("acquire success availablePermits={}",semaphore.availablePermits());


            loanReturn++;
        }

    }
    /*
    public <T> Future<T> submit(String customer,Callable<T> task) throws InterruptedException {
        CustomerLoanMsg customerLoanMsg = applyloanMsgMap.get(customer);
        int realLoan = realLoanMap.get(customer);
        Semaphore semaphore = inLoanMap.get(customer);
        if (semaphore==null||semaphore.availablePermits()==0){
            semaphore = new Semaphore(realLoan);
            inLoanMap.put(customer,semaphore);
        }
     //   log.info(semaphore.toString()+semaphore.availablePermits());
        semaphore.acquire();
       // log.info("acquire success availablePermits={}",semaphore.availablePermits());
        ExecutorService pool = findPool(customerLoanMsg.getLoanType());
        Future<T> future = pool.submit(task);
        //semaphore.release(); 不release了
        return future;
    }
    */

    /*银行分配实际 线程贷款*/
    public void realLoan(){
        int totalCount = applyloanMsgMap.values().stream().mapToInt(CustomerLoanMsg::getCount).sum();
        for (CustomerLoanMsg loanMsg : applyloanMsgMap.values()) {
            int res = core*(loanMsg.getLoanType().ordinal()+1)*loanMsg.getCount()/totalCount;
            if (res<=0)res = 1;
            realLoanMap.put(loanMsg.getCustomer(),res);
        }
    }

    private void applyLoan(CustomerLoanMsg customerLoanMsg){
        this.applyloanMsgMap.put(customerLoanMsg.getCustomer(),customerLoanMsg);
    }


    public static void main(String[] args) throws InterruptedException, ExecutionException {
        log.info("main");
        ThreBank threBank = new ThreBank();

        threBank.applyLoan("pullOrder",LoanType.IO,10);
        threBank.applyLoan("pushOrder",LoanType.IO,30);
        threBank.applyLoan("hello",LoanType.CPU,30);

        threBank.realLoan();

//        Callable<String> getOrder = ()->{
//          Thread.sleep(1000L);
//          String orderData = String.format("order%s","111111111");
//            log.info(orderData);
//            return orderData;
//        };

        Runnable getOrder = ()->{
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String orderData = String.format("order%s","111111111");
            log.info(orderData);
        };

        Runnable hello = ()->{
            log.info("hello");
        };

        List<Runnable> pullOrderTasks = Lists.newArrayList(getOrder,getOrder,getOrder,getOrder,getOrder);
        threBank.submit("pullOrder",pullOrderTasks);

        List<Runnable> helloTasks = Lists.newArrayList(hello,hello);
        threBank.submit("hello",helloTasks);

        System.out.println();
        //System.out.println(orderId);
    }

}
